//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  Low level interfaces that allow separate components to use objects that are defined
//               in completely independant libraries so long as they satisfy the basic interface requirements.
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.Interfaces
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in October, 2007.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using MapWindow.Data;
using System.Drawing;
using MapWindow.Main.EventArguments;
using MapWindow.Geometries;

namespace MapWindow.Drawing
{
    /// <summary>
    /// This can be a useful variable type but can't actually exist since all mapfeatures should actually be
    /// points, lines or polygons.
    /// </summary>
    public abstract class MapFeature: Feature, IMapFeature
    {
        #region Variables

        /// <summary>
        /// The FeatureSymbolizer for this MapFeature
        /// </summary>
        protected IFeatureSymbolizer _FeatureSymbolizer;
        
        /// <summary>
        /// The Name for this Feature
        /// </summary>
        protected string _Name;

        /// <summary>
        /// Boolean, true if this feature should reference a different feature symbolizer
        /// </summary>
        protected bool _IsEmpty;

        /// <summary>
        /// Boolean, true if this MapFeature is actively drawing
        /// </summary>
        protected bool _IsBusy;

        /// <summary>
        /// Boolean, true if the drawing should be canceled
        /// </summary>
        protected bool _CancelDrawing;
        #endregion

        #region Constructors

        /// <summary>
        /// A constructor for the shared MapFeature variables
        /// </summary>
        public MapFeature() : base()
        {
            _FeatureSymbolizer = new FeatureSymbolizer();
            _IsEmpty = false;
        }

        /// <summary>
        /// A constructor for the shared MapFeature variables
        /// </summary>
        /// <param name="fromFeature">Creates new drawing properties to associate with a specific feature</param>
        public MapFeature(IFeature fromFeature): base(fromFeature)
        {
            _FeatureSymbolizer = new FeatureSymbolizer();
            _IsEmpty = false;
        }

        /// <summary>
        ///  A constructor for the shared MapFeature variables
        /// </summary>
        /// <param name="fromFeature">Creates new drawing properties to associate with a specific feature</param>
        /// <param name="defaultSymbolizer">Specifies the drawing characteristics of a default, like a MapLayer </param>
        public MapFeature(IFeature fromFeature, IFeatureSymbolizer defaultSymbolizer)
        {
            if (defaultSymbolizer == null)
            {
                _FeatureSymbolizer = new FeatureSymbolizer();
            }
            else
            {
                _FeatureSymbolizer = defaultSymbolizer;
            }
            if (fromFeature != null)
            {
                _BasicGeometry = fromFeature.BasicGeometry;
            }
            _IsEmpty = true;
        }


        #endregion


        #region Methods

        /// <summary>
        /// This will clear the drawing characteristics of this MapPoint
        /// </summary>
        public void Clear()
        {
            _FeatureSymbolizer.Clear();
        }

        /// <summary>
        /// Draws the renderable item onto the specified graphics object.  No changes
        /// will be made to the graphics object for scaling or positioning and it is
        /// assumed that any clip-handling has been handled beforehand.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        public virtual void Draw(System.Drawing.Graphics g)
        {
            Draw(g, Rectangle.Empty, Rectangle.Empty);
        }

        /// <summary>
        /// Draws the renderable item onto the specified graphics object.  If the
        /// clipRectangle doesn't intersect the target, no drawing will take place.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <param name="clipRectangle">The bounds within which to draw</param>
        public virtual void Draw(System.Drawing.Graphics g, Rectangle clipRectangle)
        {
            Draw(g, clipRectangle, Rectangle.Empty);
        }

        /// <summary>
        /// Since this base class isn't technically a specific type yet, it doesn't know
        /// what to do when asked to draw itself.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <param name="clipRectangle">The bounds within which to draw</param>
        /// <param name="target">This specifies the pixel "Fit To" rectangle</param>
        /// <exception cref="ArgumentNullException">g cannot be null.</exception>
        /// <exception cref="ArgumentNullException">Target cannot be empty.</exception>
        public abstract void Draw(System.Drawing.Graphics g, Rectangle clipRectangle, Rectangle target);

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the color of the border, or outline for this point.
        /// If this is empty, the default for the layer will be used.
        /// </summary>
        public virtual System.Drawing.Color BorderColor
        {
            get { return _FeatureSymbolizer.BorderColor; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }
                _FeatureSymbolizer.BorderColor = value;
            }
        }

        /// <summary>
        /// Gets or sets the float thickness for the border of this point.
        /// Setting this property will also affect the BorderPen property.
        /// If this is negative or 0, the default layer thickness is used.
        /// </summary>
        public virtual float BorderWidth
        {
            get { return _FeatureSymbolizer.BorderWidth; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }  
                _FeatureSymbolizer.BorderWidth = value;
            }
        }

        /// <summary>
        /// Gets or sets the pen to use with the this point to draw the border.
        /// Setting this will also reset the BorderColor proprety.
        /// </summary>
        public virtual System.Drawing.Pen BorderPen
        {
            get { return _FeatureSymbolizer.BorderPen; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }
                _FeatureSymbolizer.BorderPen = value;
            }
        }

        /// <summary>
        /// Gets or sets a boolean value that determines whether or not the default points for this layer
        /// will draw a border.  If this property is set to false, the other border properties will be ignored.
        /// </summary>
        public virtual bool BorderVisible
        {
            get { return _FeatureSymbolizer.BorderVisible; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }
                _FeatureSymbolizer.BorderVisible = value;
            }
        }

        /// <summary>
        /// This propogates the cancel message down through the drawing levels
        /// to ensure that the current drawing action is terminated as soon
        /// as possible in order to exit gracefully.
        /// </summary>
        public virtual bool CancelDrawing
        {
            get { return _CancelDrawing; }
            set 
            {
                _CancelDrawing = value;
            }
        }

        /// <summary>
        /// Gets or sets the symbolizer that this map feature should be using.
        /// Note, this property is used by subclasses to make sure that the
        /// FeatureSymbolizer is the same as the subclass specific symbolizer
        /// </summary>
        public virtual IFeatureSymbolizer FeatureSymbolizer
        {
            get { return _FeatureSymbolizer; }
            set 
            {
                _FeatureSymbolizer = value;
            }
        }

        /// <summary>
        /// Gets or sets the solid color to be used for filling this specific point.
        /// This will also change the FillBrush to a solid brush of the specified color.
        /// </summary>
        public virtual System.Drawing.Color FillColor
        {
            get { return _FeatureSymbolizer.FillColor; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }
                _FeatureSymbolizer.FillColor = value;
            }
        }

        /// <summary>
        /// Gets or sets the System.Drawing.Brush to be used when filling this point.
        /// Setting this value will also change the FillColor property, but only if
        /// the brush is a System.Drawing.SolidBrush.  Otherwise, the color property
        /// will be ignored.
        /// </summary>
        public virtual System.Drawing.Brush FillBrush
        {
            get { return _FeatureSymbolizer.FillBrush; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }
                _FeatureSymbolizer.FillBrush = value;
            }
        }


        /// <summary>
        /// Boolean, true if the drawing code is executing
        /// </summary>
        public virtual bool IsBusy
        {
            get { return _IsBusy; }
        }


        /// <summary>
        /// Gets a boolean value.  If the value is true, then the default symbolizer
        /// is being used.  If the value is false, then a new PolygonSymbolizer
        /// exists describing this specific shape, but any properties that are not
        /// explictly set will fall through to the properties on the original
        /// default symbolizer.
        /// </summary>
        public virtual bool IsEmpty
        {
            get { return _IsEmpty; }
        }


        /// <summary>
        /// Gets or sets a string name to help identify this MapFeature
        /// </summary>
        public virtual string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }


        /// <summary>
        /// Gets or sets a float value from 0 to 1 where 0 is completely transparent
        /// and 1 is completely solid.  Setting an alpha of a specific feature, like
        /// FillColor, to something less than 255 will control that feature's transparency
        /// without affecting the others.  The final transparency of the feature will
        /// be that alpha multiplied by this value and rounded to the nearest byte.
        /// </summary>
        public virtual float Opacity
        {
            get { return _FeatureSymbolizer.Opacity; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }
                _FeatureSymbolizer.Opacity = value;
            }
        }


        /// <summary>
        /// Gets or Sets a ScaleModes enumeration that determines whether non-coordinate drawing
        /// properties like width or size use pixels or world coordinates.  If pixels are
        /// specified, a back transform is used to approximate pixel sizes. 
        /// </summary>
        public virtual ScaleModes ScaleMode
        {
            get { return _FeatureSymbolizer.ScaleMode; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }
                _FeatureSymbolizer.ScaleMode = value;
            }
        }

        /// <summary>
        /// Gets or sets a boolean indicating whether or not this specific MapPoint should be drawn.
        /// </summary>
        public virtual bool Visible
        {
            get { return _FeatureSymbolizer.Visible; }
            set 
            {
                if (_IsEmpty == true)
                {
                    _FeatureSymbolizer = new FeatureSymbolizer(_FeatureSymbolizer);
                }
                _FeatureSymbolizer.Visible = value;
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// Fires before the paint event.  If the cancel property of this event is set to true,
        /// the paint event will be canceled.  The rect property is the "fit-to" property for
        /// this event.
        /// </summary>
        public virtual event EventHandler<PaintCancel> BeforePainting;

        /// <summary>
        /// Fires the BeforeDrawing event
        /// </summary>
        /// <param name="g"></param>
        /// <param name="clipRect"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        protected virtual bool OnBeforePainting(System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {

            if (BeforePainting != null)
            {
                PaintCancel e = new PaintCancel(g, clipRect, target);
                BeforePainting(this, e);
                if (e.Cancel == true) return true;
            }

            return false;
        }

        /// <summary>
        /// Fires after the paint event is complete.  The "clipRectangle" was actually the 
        /// "Fit-To" rectangle that the frame is drawn within.
        /// </summary>
        public virtual event EventHandler<PaintCompleted> AfterPainting;

        /// <summary>
        /// The drawing method finished, and exited normally.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface used by the draw method.</param>
        /// <param name="clipRect">The System.Drawing.Rectangle defining the area to actually perform drawing</param>
        /// <param name="target">The destination rectangle for the System.Drawing.Rectangle where the result should be drawn</param>
        protected virtual void OnAfterPainting(System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {
            if (AfterPainting != null)
            {
                AfterPainting(this, new PaintCompleted(g, clipRect, target));
            }
        }

        /// <summary>
        /// Internal Method to fire the Cancel Event.  This caries the same basic information that the 
        /// DrawingComplete method, plus an exception if any as well as a boolean indicating the draw method was cancelled.
        /// </summary>
        /// <param name="Error">A System.Exception showing the error that caused the cancelation</param>
        /// <param name="g">A System.Drawing.Graphics surface that drawing was taking place on</param>
        /// <param name="clipRect">The ClipRectangle or Rectangle.Empty</param>
        /// <param name="target">The Destination Rectangle or Rectangle.Empty</param>
        protected virtual void OnPaintCancelled(System.Exception Error, System.Drawing.Graphics g, System.Drawing.Rectangle clipRect, System.Drawing.Rectangle target)
        {
            if (AfterPainting != null)
            {
                AfterPainting(this, new PaintCompleted(Error, true, g, clipRect, target));
            }
        }


        #endregion



    }
}
